﻿using System;
using System.Reflection;
using SimpleWorkflow.Data;
using SimpleWorkflow.Patterns;
using SimpleWorkflow.Steps;

namespace SimpleWorkflow.Core
{
    public class Generator<T> where T : ISharedFlowContext
    {
        private SimpleFlow<T>.RunProxyDelegate RunProxy { get; set; }
        private FlowContext WorkflowContext { get; set; }

        public Generator(FlowContext workflowContext, SimpleFlow<T>.RunProxyDelegate runProxy)
        {
            WorkflowContext = workflowContext;
            RunProxy = runProxy;
        }

        public SimpleStep<T> FromSegment<TSegment>(TSegment segment) where TSegment : Segment
        {
            if (segment is ContainerSegment)
                return FromContainerSegment(segment as ContainerSegment);
            if (segment is SimpleSegment)
                return FromSimpleSegment(segment as SimpleSegment);

            throw new Exception("Segment type not yet mapped");
        }

        private SimpleStep<T> FromContainerSegment(ContainerSegment containerSegment)
        {
            var containerStep = FromSimpleSegment(containerSegment);

            foreach (var step in containerSegment.Segments)
                ((ILinearSteps)containerStep).AddStep(FromSegment(step));

            return containerStep;
        }

        private SimpleStep<T> FromSimpleSegment(SimpleSegment simpleSegment)
        {
            var step = (SimpleStep<T>)Activator.CreateInstance(simpleSegment.StepType);
            step.WorkflowContext = WorkflowContext;
            step.RunProxy = RunProxy;
            foreach (var fieldInfo in step.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
                if (fieldInfo.FieldType.IsGenericType && fieldInfo.FieldType.GetGenericTypeDefinition() == typeof(Reference<>))
                {
                    var reference = (IReference)Activator.CreateInstance(fieldInfo.FieldType);
                    reference.Name = fieldInfo.Name;
                    reference.WorkflowContext = WorkflowContext;
                    WorkflowContext.AddReference(reference);
                    fieldInfo.SetValue(step, reference);
                }

            foreach (var designerProperty in simpleSegment.ComplexStepExtensionValues)
            {
                var property = step.GetType().GetProperty(designerProperty.Key);
                if (property != null && property.GetCustomAttributes(typeof(ComplexStepExtensionAttribute), true).Length == 1)
                    step.GetType().GetProperty(designerProperty.Key).SetValue(step, designerProperty.Value, null);
            }

            return step;
        }

    }
}
